package com.job.framework.config.sharding;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.NoneShardingStrategyConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

/**
 * sharding分片数据源
 *
 * @author sunq
 */
@Configuration
public class ShardingDatasourceConfig {

    /**
     * 逻辑表名称
     */
    private final String LOGICAL_TABLE = "tbl_dev_data_hour";

    /**
     * 分片键
     */
    private final String DATABASE_SHARDING_COLUMN = "record_time";


    @Bean(name = "shardingJdbcDataSource")
    @ConfigurationProperties("spring.datasource.druid.sharding")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.sharding", name = "enabled", havingValue = "true")
    public DataSource shardingJdbcDataSource(@Qualifier("slaveDataSource") DataSource slaveDataSource) throws SQLException {

        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("slaveDataSource", slaveDataSource);

        // 分片规则配置对象
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // 规则配置
        shardingRuleConfig.getTableRuleConfigs().add(getDevDataTableRuleConfiguration());
        // 默认库，如果存在广播表和绑定表也可在此配置
        shardingRuleConfig.setDefaultDataSourceName("slaveDataSource");
        // 开启日志打印
        final Properties properties = new Properties();
        properties.setProperty("sql.show", "true");
        // 返回数据源对象
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, properties);
    }

    TableRuleConfiguration getDevDataTableRuleConfiguration() {

        // 暂定为两年，关于此表达式，可查看官方文档
        // https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/sharding/other-features/inline-expression/
        String inLineExpressionStr = "" +
                "slaveDataSource." + LOGICAL_TABLE + "_20211${0..2}" + "," +
                "slaveDataSource." + LOGICAL_TABLE + "_20220${1..9}" + "," +
                "slaveDataSource." + LOGICAL_TABLE + "_20221${0..2}";

        final TableRuleConfiguration ruleConfiguration = new TableRuleConfiguration(LOGICAL_TABLE,inLineExpressionStr);
        //分库策略，不分库
        ruleConfiguration.setDatabaseShardingStrategyConfig(new NoneShardingStrategyConfiguration());
        // 表分片算法（精确、范围），按月分表
        ruleConfiguration.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration(DATABASE_SHARDING_COLUMN, new PreciseTableShardingAlgorithm(), new RangeTableShardingAlgorithm())
        );

        // 设置主键生成策略
        ruleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id"));
        return ruleConfiguration;
    }

    /**
     * 动态生成表分片集合，但是只有项目启动时生效
     * @return
     */
    String genInlineExpressionStr() {
        List<String> list = new ArrayList<>();
        int start = Integer.parseInt("202109");
        int end = Integer.parseInt(DateUtil.format(new Date(), DatePattern.SIMPLE_MONTH_PATTERN));
        for (; start <= end; start++) {
            list.add("slaveDataSource." + LOGICAL_TABLE + "_" + start);
        }

        return CollUtil.join(list, ",");
    }


}
